﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using Dexter.CallContext;
using Dexter.Domain.Model;
using Dexter.Logger.Contracts;
using Dexter.Scheduler;
using Dexter.Services;

namespace Dexter.Job.EmailSender {
	/// <summary>
	/// The implementation of <see cref="IFrequentlyJob"/>. This job will send the queued email.
	/// </summary>
	public class EmailSenderJob : IFrequentlyJob {
		readonly IConfigurationService configurationService;
		readonly IEmailService emailService;
		readonly ILogger logger;
		readonly IDexterCall dexterCall;

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "EmailSenderJob" /> class.
		/// </summary>
		/// <param name = "logger">The logger.</param>
		/// <param name = "configurationService">The configuration service.</param>
		/// <param name = "emailService">The email service.</param>
		public EmailSenderJob ( ILogger logger , IConfigurationService configurationService , IEmailService emailService , IDexterCall dexterCall ) {
			this.logger = logger;
			this.dexterCall = dexterCall;
			this.configurationService = configurationService;
			this.emailService = emailService;
		}

		/// <summary>
		/// 	The elapsed minutes from a job execution and the next one.
		/// </summary>
		/// <value>The minutes.</value>
		public int Minutes {
			get { return 10; }
		}

		#region IJob Members

		/// <summary>
		/// Gets a value indicating whether [auto manage session].
		/// </summary>
		/// <value><c>true</c> if [auto manage session]; otherwise, <c>false</c>.</value>
		public bool AutoManageSession {
			get { return false; }
		}

		/// <summary>
		/// 	Gets the name of the group.
		/// </summary>
		/// <value>The name of the group.</value>
		public string GroupName {
			get { return null; }
		}

		/// <summary>
		/// 	Set to <c>false</c> if the state of Job will be reused from the next startup. Otherwise <c>false</c>.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is volatile; otherwise, <c>false</c>.
		/// </value>
		public bool IsVolatile {
			get { return true; }
		}

		/// <summary>
		/// 	This method will be exececuted by the <see cref = "IScheduler" />
		/// </summary>
		/// <remarks>
		/// 	The execution interval depends by <see cref = "ICronJob" /> or <see cref = "IFrequentlyJob" />.
		/// </remarks>
		public void Execute ( ) {
			logger.DebugFormat ( "Executing 'EmailSenderJob'", this.GetType ( ).FullName );

			IEnumerable <EmailMessage> emails;

			try {
				dexterCall.StartSession ( false );

				emails = emailService.Peek ( 5 );

				dexterCall.Complete ( true );
			}
			catch ( Exception ex) {
				dexterCall.Complete ( false );
				logger.Error ( ex.Message, ex );
				throw;
			}
			
			if (emails == null || emails.Count ( ) < 1 ) {
				return;
			}

			foreach ( var email in emails ) {
				if ( email == null ) {
					logger.Fatal ( "Houston we have a problem!" );
					continue;
				}

				Send ( email );
			}

			logger.DebugFormat ( "'EmailSenderJob' execution complete" , this.GetType ( ).FullName );
		}

		/// <summary>
		/// Gets the unique id.
		/// </summary>
		/// <value>The unique id.</value>
		public string UniqueId {
			get { return "{3afe048e-187e-43b1-84ef-13858b3ed912}"; }
		}

		#endregion

		void Queue(EmailMessage emailMessage) {
			try {
				dexterCall.StartSession(false);
				emailService.Queue ( emailMessage );
				dexterCall.Complete ( true );
			}
			catch{
				dexterCall.Complete ( false );
			}
		}

		void Dequeue ( EmailMessage emailMessage ) {
			try {
				dexterCall.StartSession ( false );
				emailService.Dequeue ( emailMessage );
				dexterCall.Complete ( true );
			}
			catch {
				dexterCall.Complete ( false );
			}
		}

		void Send ( EmailMessage emailMessage ) {
			bool sent = false;
			var configuration = configurationService.Configuration;

			using ( var smtp = new SmtpClient ( configuration.SmtpConfiguration.SmtpHost , configuration.SmtpConfiguration.Port ) ) {
				
				smtp.EnableSsl = configuration.SmtpConfiguration.UseSSL;
				smtp.Credentials = new NetworkCredential ( configuration.SmtpConfiguration.Username , configuration.SmtpConfiguration.Password );

				using ( var msg = emailMessage.ToMailMessage ( ) ) {
					try {
						smtp.Send ( msg );
						logger.DebugFormat ( "Email sent! - Subject: {0} - To {1}", emailMessage.Subject, emailMessage.MailTo );
						sent = true;
					}
					catch ( SmtpException e ) {
						logger.Error ( e.Message , e );
						emailMessage.Status = "Unable to send this message";

						Queue ( emailMessage );
					}
				}

				if(sent)
					Dequeue ( emailMessage );
			}
		}
	}
}